/**
 * DOS头结构体类，用于解析PE文件中的DOS头信息。
 * @author tongxin
 * @date 2024/4/18 0:27
 */


export class DosHeader {
  // DOS头的固定大小
  static DOS_HEADER_SIZE = 64 as const;
// DOS头的魔数，标识一个PE文件的开始
  static DOS_HEADER_MAGIC = 0x5a4d as const;
// DOS头魔数在文件中的偏移位置
  static DOS_HEADER_MAGIC_OFFSET = 0x3C as const;

  // MZ签名
  signature: number;
  // 最后一个页面中使用的字节数
  usedBytesInLastPage: number;
  // 页面中的文件大小
  fileSizeInPage: number;
  // 迁移项的数量
  numberOfRelocationItems: number;
  // 头部大小（段）
  headerSizeInParagraphs: number;
  // 最小额外段数
  minExtraParagraphs: number;
  // 最大额外段数
  maxExtraParagraphs: number;
  // 初始相对SS值
  initialRelativeSS: number;
  // 初始SP值
  initialSP: number;
  // 校验和
  checksum: number;
  // 初始IP值
  initialIP: number;
  // 初始相对CS值
  initialRelativeCS: number;
  // 迁移表地址
  addressOfRelocationTable: number;
  // 覆盖号
  overlayNumber: number;
  // 保留字段1
  reserved1: number[];
  // OEM ID
  oemId: number;
  // OEM信息
  oemInfo: number;
  // 保留字段2
  reserved2: number[];
  // nt头的地址
  addressOfNewExeHeader: number;

  /**
   * 根据字节数组构建DOS头对象。
   * @param byteArray 包含DOS头信息的字节数组
   * @throws IOException 如果解析过程中遇到错误，或者MZ签名不匹配，则抛出此异常
   */
  constructor(byteArray: IByteArray) {
    // 读取并验证MZ签名
    const mzSignature = byteArray.readWord();
    if (DosHeader.DOS_HEADER_MAGIC != mzSignature) {
      throw new Error("Invalid PE file: Missing MZ signature");
    }
    this.signature = mzSignature;

    // 读取DOS头的其他字段
    this.usedBytesInLastPage = byteArray.readWord();
    this.fileSizeInPage = byteArray.readWord();
    this.numberOfRelocationItems = byteArray.readWord();
    this.headerSizeInParagraphs = byteArray.readWord();
    this.minExtraParagraphs = byteArray.readWord();
    this.maxExtraParagraphs = byteArray.readWord();
    this.initialRelativeSS = byteArray.readWord();
    this.initialSP = byteArray.readWord();
    this.checksum = byteArray.readWord();
    this.initialIP = byteArray.readWord();
    this.initialRelativeCS = byteArray.readWord();
    this.addressOfRelocationTable = byteArray.readWord();
    this.overlayNumber = byteArray.readWord();

    // 读取保留字段1
    this.reserved1 = [];
    for (let i = 0; i < 4; i++) {
      this.reserved1[i] = byteArray.readWord();
    }

    // 读取OEM ID和信息
    this.oemId = byteArray.readWord();
    this.oemInfo = byteArray.readWord();

    // 读取保留字段2
    this.reserved2 = [];
    for (let i = 0; i < 10; i++) {
      this.reserved2[i] = byteArray.readWord();
    }

    // 读取nt头的地址
    this.addressOfNewExeHeader = byteArray.readDWord();
  }
}
